import copy
import logging
import os
from django.contrib.auth.models import User
from api_test.models import LoadFile, ApiDataType, Project, ApiDataStructure, ApiInfo, ApiParameter, ApiResponse, \
    ApiGroupLevelFirst, ApiVersionLevelFirst
from api_test.serializers import ApiInfoDeserializer, ApiParameterDeserializer, ApiResponseDeserializer, \
    ApiDataTypeDeserializer, ApiDataTypeSerializer, ApiDataStructureDeserializer
from fotoable_api_test import settings
import xml.etree.ElementTree as ET

logger = logging.getLogger(__name__)  # 这里使用 __name__ 动态搜索定义的 logger 配置，这里有一个层次关系的知识点。


def read_pb_file(bp_file_path):
    api_datas = []
    pb_file = open(bp_file_path, "r+")
    pb_file_str = pb_file.read()
    pb_str = dict(eval(pb_file_str.replace("true", "True").replace("false", "False")))
    for file in pb_str["files"]:
        file_datas = file["messages"]
        for file_data in file_datas:
            api_datas.append(file_data)
    return api_datas


def add_project_datatype(project_id, datatypes):
    data_types = {}
    pro = Project.objects.get(id=project_id, status=True)
    for type_name, type_data in datatypes.items():
        type_data["project_id"] = project_id
        obj = ApiDataType.objects.filter(project=project_id, type=type_name, status=True)
        serializer = ApiDataTypeDeserializer(data=type_data)
        if serializer.is_valid():
            if obj.count() > 0:
                type_data["id"] = obj[0].id
                serializer.update(instance=obj[0], validated_data=type_data)
            else:
                serializer.save(project=pro)
        else:
            print(serializer.errors)
    obi = ApiDataType.objects.filter(project=project_id, status=True)
    datatype_list = ApiDataTypeSerializer(obi, many=True).data
    for each in datatype_list:
        each = dict(each)  # OrderDict转dict
        data_types[each["type"]] = each
    return data_types


def add_structure_data(project_id, data_structures, type_datas):
    for data_structure_name, data_structure in data_structures.items():
        type_id = type_datas[data_structure_name]["id"]  # 获取复杂数据类型id
        # TODO 写入复杂结构表，先删除所有该type_id的子类型数据
        ApiDataStructure.objects.filter(type=type_id).delete()
        obj = ApiDataType.objects.get(id=type_id, project=project_id)
        for data_structure_param in data_structure:
            type_name = data_structure_param["type"]  # 获取数据类型名称
            type_sub_id = type_datas[type_name]["id"]
            obk = ApiDataType.objects.get(id=type_sub_id, project=project_id)
            data_structure_param["project_id"] = project_id
            data_structure_param["type_id"] = type_id
            data_structure_param["type_sub_id"] = type_sub_id  # 获取子类型id
            serializer = ApiDataStructureDeserializer(data=data_structure_param)
            if serializer.is_valid():
                serializer.save(type=obj, type_sub=obk)
            else:
                print(serializer.errors)


def add_api_info(project_id, apiVersion_id, apiModule_id, user, api_datas):
    api_ids = []
    obj = Project.objects.get(id=project_id, status=True)
    for api_name, data in api_datas.items():
        data["project_id"] = project_id
        data["userUpdate_id"] = user
        serialize = ApiInfoDeserializer(data=data)
        # TODO 写入apiparam、apiresponse表前，先删除该api的数据; 写入apiinfo表前先删除，如果原先字段不为空更新的字段为空，则该字段不会修改为空
        obi = ApiInfo.objects.filter(name=api_name, project=project_id, status=True)
        if obi.count() > 0:
            ApiParameter.objects.filter(api=obi[0].id).delete()
            ApiResponse.objects.filter(api=obi[0].id).delete()
            obi.delete()
        if serialize.is_valid():
            # api表
            obm = ApiGroupLevelFirst.objects.get(id=apiModule_id, project=project_id, status=True)
            obv = ApiVersionLevelFirst.objects.get(id=apiVersion_id, project=project_id, status=True)
            obu = User.objects.get(id=user)
            serialize.save(project=obj, apiModule=obm, apiVersion=obv, userUpdate=obu)
            # param 以及 response表
            api_id = ApiInfo.objects.get(name=api_name, project=project_id, status=True).id
            api_ids.append(api_id)
            if data.get("requestList") and len(data.get("requestList")):
                for i in data["requestList"]:
                    # print(i)
                    if i.get("name"):
                        i["api"] = api_id
                    else:
                        # TODO 参数列表有误
                        print("请求参数列表缺少name字段")

                    # TODO  因为requestList集合中每条记录的type_id值可能不同，所以不能用many=true
                    # param_serialize = ApiParameterDeserializer(data=data["requestList"], many=True)
                    param_serialize = ApiParameterDeserializer(data=i)
                    if param_serialize.is_valid():
                        obtype = ApiDataType.objects.get(id=i["type_id"], project=data["project_id"], status=True)
                        param_serialize.save(api=ApiInfo.objects.get(id=api_id), type=obtype)

            if data.get("responseList") and len(data.get("responseList")):
                for i in data["responseList"]:
                    if i.get("name"):
                        i["api"] = api_id
                    else:
                        # TODO 响应参数列表有误
                        print("参数列表缺少name字段")
                # response_serialize = ApiResponseDeserializer(data=data["responseList"], many=True)
                    response_serialize = ApiResponseDeserializer(data=i)
                    if response_serialize.is_valid():
                        obtype = ApiDataType.objects.get(id=i["type_id"], project=data["project_id"], status=True)
                        response_serialize.save(api=ApiInfo.objects.get(id=api_id), type=obtype)
        else:
            print(serialize.errors)
    return api_ids


def import_api(type, ids, project, apiVersion, apiModule, user, address, protocol, requestType):
    # 根据id从数据库中获取文件，根据文件后缀判断是proto or json or xml
    file_dir = os.path.join(settings.MEDIA_ROOT, 'loadfile/')
    api_datas = {}
    # 若为pb文件，需先将content写入loadfile中，使用protoc命令将其转为json文件
    if type == "proto":
        proto_file_name = ""
        for i in ids:
            file_load = LoadFile.objects.get(id=i, project_id=project, status=True)
            filepath = file_load.file.path
            file_path, file_type = filepath.split(".")
            file_name = file_path.split('/')[-1]
            if file_type != 'proto':
                continue
            proto_file_name += file_name + ".proto "
        cmd = "cd %s; protoc-3.7.1 --doc_out=./ --doc_opt=json,ConvertTo.json %s" % (file_dir, proto_file_name)
        logging.info(cmd)
        os.system(cmd)

        get_data = GetAPIDataPb()
        # 解析json文件
        api_datas = get_data.get_data(file_dir + "ConvertTo.json")
    elif type == "xml":
        file_load = LoadFile.objects.get(id=ids[0], project_id=project, status=True)
        filepath = file_load.file.path
        get_data = GetAPIDataXML()
        # 解析xml文件
        api_datas = get_data.get_data(filepath)


    # print(get_data.data_type)
    # 添加数据类型
    type_datas = add_project_datatype(project, get_data.data_type)
    # print(type_datas)
    # # 对api数据进行编辑
    get_data.edit_data(api_datas, project, apiVersion, apiModule, type_datas, address, protocol, requestType)
    # # 添加复杂数据类型
    add_structure_data(project, get_data.api_data_structures, type_datas)
    # # 添加api 参数 响应等信息
    api_ids = add_api_info(project, apiVersion, apiModule, user, get_data.api_datas)
    return api_ids


class GetAPIDataPb(object):
    def __init__(self):

        self.data_type = {}  # 所有的数据类型，包括复杂结构
        self.api_data_structures = {}  # 所有复杂数据结构
        self.api_datas = {}
        self.proto_type_table = [
            "float",
            "int32",
            "int64",
            "uint32",
            "uint64",
            "sint32",
            "sint64",
            "fixed32",
            "fixed64",
            "fixed64",
            "sfixed32",
            "sfixed64",
            "bool",
            "string",
            "bytes",
        ]
        self.api_sign = {"R": "requestList",
                    "Q": "responseList",
                    "Request": "requestList",
                    "Response": "responseList",
                    "s2c": "responseList",
                    "c2s": "requestList"}

    def get_data_type(self, data_type):
        # 获得数据类型
        if data_type not in self.data_type:
            data = {"project_id": "", "type": data_type, "description": ""}
            if data_type not in self.proto_type_table:
                data.update({"complex": 1})
            self.data_type.update({data_type: data})

    def get_api_name(self, api_name):
        for k, v in self.api_sign.items():
            if k in api_name:
                api_name = api_name.replace(k, "", 1)
                api_name = api_name.strip("_")
                return api_name, v
        return api_name, 0

    def get_data(self, bp_file_path):
        datas = {}
        api_infos = read_pb_file(bp_file_path)
        for api_info in api_infos:
            new_api_params = []
            api_name = api_info["name"]
            api_description = api_info["description"]
            api_params = api_info["fields"]
            for api_param in api_params:
                api_param_name = api_param["name"]  # 获得参数名称
                api_param_type = api_param["type"]  # 获得参数类型
                api_param_description = api_param["description"]  # 获得参数描述
                api_param_label = api_param["label"]  # 获得参数标签，必须，可选，是否为数组
                api_param_required = False  # 是的必须
                api_param_repeated = False  # 是否为list
                if api_param_label == "repeated":
                    api_param_required = True
                    api_param_repeated = True
                elif api_param_label == "required" or api_param_label == "":
                    api_param_required = True
                    api_param_repeated = False
                self.get_data_type(api_param_type)
                new_api_params.append({
                    "name": api_param_name,
                    "required": api_param_required,
                    "description": api_param_description,
                    "repeated": api_param_repeated,
                    "type": api_param_type,
                })
            if api_name not in datas:
                new_api_data = {
                    "name": api_name,
                    "info": new_api_params,
                    "description": api_description
                }
                datas.update({api_name: new_api_data})
        return datas

    def edit_data(self, datas, project_id, version_id, modules_id, type_datas, address, protocol, requestType):
        for api_name, api_value in datas.items():
            # 获得复杂结构
            if api_name in self.data_type:
                # self.api_data_structures[api_name] = copy.deepcopy(api_value["info"])
                self.api_data_structures[api_name] = copy.deepcopy(api_value["info"])
            new_api_name, api_sign = self.get_api_name(api_name)
            # 对数据格式进行编辑，方便上传
            if api_sign != 0 and len(api_value["info"]) > 0:
                params = api_value["info"]
                api_value.pop("info")
                for param in params:
                    param["type_id"] = type_datas[param["type"]]["id"]
                    param.pop("type")

                if new_api_name not in self.api_datas:
                    api_value[api_sign] = params
                    api_value["name"] = new_api_name
                    api_value["project_id"] = project_id
                    api_value["apiModule_id"] = modules_id
                    api_value["apiVersion_id"] = version_id
                    api_value["structure"] = "Protobuf"

                    api_value["apiAddress"] = address
                    api_value["protocol"] = protocol
                    api_value["requestType"] = requestType
                    self.api_datas.update({new_api_name: api_value})
                else:
                    self.api_datas[new_api_name][api_sign] = params


class GetAPIDataXML(object):
    def __init__(self):
        self.api_datas = {}

        self.api_datas_response = {}

        self.data_type = {}

        self.api_data_structures = {}

        # 根据messagedId区分请求参数与响应参数 因为node alias名称不一定对应
        self.request_response = {}

    def get_data(self, xml_file_path):
        datas = {}
        tree = ET.ElementTree(file=xml_file_path)
        # 获得根节点
        root = tree.getroot()
        for node in root.iter(tag='node'):
            node_attrib_alias = node.attrib.get("alias")

            # 获取请求or复杂类型的描述
            property_dict = {}
            for pros in node.findall('properties/property'):
                pro_attrib = pros.attrib
                property_dict[pro_attrib.get("key")] = pro_attrib.get("value")

            # 获取请求参数or复杂类型子结构
            field_list = []
            for field in node.iter(tag='field'):
                field_attrib = field.attrib

                field_attrib["description"] = field_attrib.pop('desc')
                field_attrib.pop("sort")
                if 'generic' in field_attrib:
                    field_attrib["repeated"] = 1 if field_attrib.get("alias") == 'list' else 0
                    field_attrib["type"] = field_attrib.pop('generic')
                    field_attrib.pop("alias")
                else:
                    field_attrib["type"] = field_attrib.pop('alias')

                # 简单数据类型
                if field_attrib["type"] not in self.data_type:
                    simple_datatype_param = {"type": field_attrib["type"]}
                    self.data_type[field_attrib["type"]] = simple_datatype_param

                field_list.append(field_attrib)
                # print(field_list)

            if node_attrib_alias.startswith("CS"):  # node为请求 field为请求参数or响应参数

                # 将messageId传入参数列表
                messageId_param = {"name": "messageId", "type": 'int', "description": property_dict["messageId"]}
                field_list.append(messageId_param)

                param_list = {"name": node_attrib_alias[2:], "description": property_dict["desc"],
                              "requestList": field_list}
                datas[node_attrib_alias[2:]] = param_list
                self.request_response[node_attrib_alias[2:]] = int(property_dict["messageId"])
            elif node_attrib_alias.startswith("SC"):

                # 将messageId传入参数列表
                messageId_param = {"name": "messageId", "type": 'int', "description": property_dict["messageId"]}
                field_list.append(messageId_param)

                param_list = {"responseList": field_list}
                self.api_datas_response[node_attrib_alias] = param_list
                self.request_response[int(property_dict["messageId"])] = node_attrib_alias
            else:  # 表示node为复杂数据类型, field为类型子结构
                complex_datatype_param = {"type": node_attrib_alias, "complex": 1,
                                          "description": property_dict.get("desc")}
                self.data_type[node_attrib_alias] = complex_datatype_param
                self.api_data_structures[node_attrib_alias] = field_list
        return datas

    def edit_data(self, datas, project_id, version_id, modules_id, type_datas, address, protocol, requestType):
        for api_name, params in datas.items():
            respponse_name = self.request_response[self.request_response[api_name] + 1]
            params.update(self.api_datas_response[respponse_name])

            for req in params["requestList"]:
                req["type_id"] = type_datas[req["type"]]["id"]
                req.pop("type")
            for res in params["responseList"]:
                res["type_id"] = type_datas[res["type"]]["id"]
                res.pop("type")

            params["project_id"] = project_id
            params["apiModule_id"] = modules_id
            params["apiVersion_id"] = version_id
            params["structure"] = "Xml"

            params["apiAddress"] = address
            params["protocol"] = protocol
            params["requestType"] = requestType

        self.api_datas = datas
